import os
import ctypes

import py3func
from executor.abstractcmd import KeyCmd
from cui import Enum_Btn_Status

LOG = py3func.make_logging(__file__)
DEFAULT_OUTPUT_OFFSET = 1000

class VirtualPLC(KeyCmd):
    def __new__(cls):
        if not hasattr(cls, "singleton_instance"):
            cls.singleton_instance = super().__new__(cls)
            cls.init()
        return cls.singleton_instance  # 返回单例对象

    @classmethod
    def init(self):
        self.plc_ready = False
        try:
            path_nut = os.environ["NUTHOME"]
            path_libplc = path_nut + "/lib/libnutaccess.so"
            self.lib_plc = ctypes.CDLL(path_libplc)
            self.init_PycLib()
        except KeyError:
            LOG.exception("未发现NUTHOME目录及PLC相关lib库")

    @classmethod
    def init_PycLib(self):
        if self.lib_plc.PycGetM():
            LOG.debug("PycNutAccess Lib has been initialized.")
            self.plc_ready = True
        else:
            LOG.error("Fail to initialize the lib -- PycNutAccess")
            # self.plc_ready = False  # stay the same.

    def update_input_address(self, address: str, output_addr=None):
        try:
            self.in_byte, self.in_bit = [int(x) for x in address.split(".")]
            if output_addr:
                self.out_byte, self.out_bit = [int(x) for x in output_addr.split(".")]
            else:
                self.out_byte = self.in_byte + DEFAULT_OUTPUT_OFFSET
                self.out_bit = self.in_bit
        except ValueError:  # 未发现split(".")
            raise Exception("The xml data for PLC address miss a dot separator")

    def key_exec(self, key_status):
        LOG.debug("PLC input address: %d.%df" % (self.in_byte, self.in_bit))
        if self.plc_ready:
            # 按状态取反，用于NonKeyCmd
            # val_current = self.lib_plc.PycGetMBit(self.byte, self.bit)
            # self.lib_plc.PycSetMBit(self.byte, self.bit, not val_current)

            # 按ButtonStatus写入值
            new_value = 0 if key_status == Enum_Btn_Status["released"] else 1
            self.lib_plc.PycSetMBit(self.in_byte, self.in_bit, new_value)

    def get_output_status(self):
        """ return the output-value as an int num of Enum_Btn_Status """
        if self.plc_ready:
            val_current = self.lib_plc.PycGetMBit(self.out_byte, self.out_bit)
            return Enum_Btn_Status["pressed"] if val_current else Enum_Btn_Status["released"]
